package com.wxboot.game.eliminate.modules.service;

import com.wxboot.game.eliminate.base.BombAreaPoint;
import com.wxboot.game.eliminate.base.JellyCell;
import com.wxboot.game.eliminate.base.JellyContext;
import com.wxboot.game.eliminate.modules.service.strategy.BombStrategy;
import com.wxboot.game.eliminate.modules.service.strategy.BombStrategyFactory;
import com.wxboot.game.eliminate.support.JellyBeanGenerator;
import com.wxboot.game.eliminate.util.JellyPrintUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.invoke.MethodHandles;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.function.BiConsumer;
import java.util.function.IntPredicate;

import static com.wxboot.game.eliminate.config.JellyConstant.CELL_NUM;

public class JellyEliminateExecutor {

    private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    private JellyContext jellyContext;

    public JellyEliminateExecutor(JellyContext jellyContext) {
        this.jellyContext = jellyContext;
    }

    public void invokeMove(BombAreaPoint bombAreaPoint) {
        this.fireJellyBomb(bombAreaPoint);
        this.compressAndFillBombedCell();
    }

    private void fireJellyBomb(BombAreaPoint bombAreaPoint) {
        Queue<BombAreaPoint> queue = new ArrayDeque<>();
        BombAreaPoint curPoint = null;
        queue.offer(bombAreaPoint);
        while (!queue.isEmpty()) {
            // 开始进行爆破
            curPoint = queue.poll();
            this.bombing(curPoint, queue);
        }
    }

    private void bombing(BombAreaPoint bombAreaPoint, Queue<BombAreaPoint> queue) {
        System.out.println(bombAreaPoint);
        // 迭代获取区域内是否存在果冻炸弹，如果存在，则计算爆破范围坐标
        int row = bombAreaPoint.getLeftUpRow();
        int col = bombAreaPoint.getLeftUpCol();
        while (row <= bombAreaPoint.getRightDownRow()
                && col <= bombAreaPoint.getRightDownCol()) {
            this.bombingSingle(row, col, queue);
            col++;
            if (col > bombAreaPoint.getRightDownCol()) {
                row++;
                col = bombAreaPoint.getLeftUpCol();
            }
        }
    }

    private void bombingSingle(int row, int col, Queue<BombAreaPoint> queue) {
        JellyCell[] jellyCells = this.jellyContext.getMatrixJellyCell()[row];
        if (jellyCells == null) {
            return;
        }

        JellyCell jellyCell = jellyCells[col];
        jellyCells[col] = null;
        // 已经爆炸，直接返回
        if (jellyCell == null || this.jellyContext.getBombed().contains(jellyCell)) {
            return;
        }

        BombStrategy bombStrategy = BombStrategyFactory.getBombStrategy(jellyCell.getJellyType());
        if (bombStrategy != null) {
            BombAreaPoint bombing = bombStrategy.getBombAreaPoint(row, col);
            queue.offer(bombing);
        }

        // 根据不同爆炸类型，将待爆炸果冻区域坐标加入队列
        if (jellyCell.isHorizoncal()) {
            // 记录水平爆炸的整行row
            this.jellyContext.getTotalRowBombedSet().add(row);
        }

        this.jellyContext.incrCount();
        // 记入已爆破集合
        this.jellyContext.getBombed().add(jellyCell);
    }

    /**
     * 压缩以及填充已爆破的宫格
     * 7 6 5 4 3 2 1
     * n 6 5 n n 2 n
     */
    public void compressAndFillBombedCell() {
        // 整行压缩
        LOGGER.info("total row compressed---------------");

        this.compressTotalRow();
        JellyPrintUtil.printMatrixJellyCell(this.jellyContext.getMatrixJellyCell());

        LOGGER.info("col compressed---------------");
        this.compressCol();
        JellyPrintUtil.printMatrixJellyCell(this.jellyContext.getMatrixJellyCell());
    }

    private void compressCol() {
        // 有多少条整行果冻被整行消除，则进行列压缩时就截止到该行
        JellyCell[][] matrixJellyCell = this.jellyContext.getMatrixJellyCell();
        int lastRow = this.jellyContext.getTotalRowBombedSet().size();
        for (int i = 0; i < CELL_NUM; i++) {
            final int fi = i;
            IntPredicate predicate =
                    fastTemp -> matrixJellyCell[fastTemp] == null || matrixJellyCell[fastTemp][fi] == null;
            BiConsumer<Integer, Integer> compressConsumer =
                    (slowTemp, fastTemp) -> matrixJellyCell[slowTemp][fi] = matrixJellyCell[fastTemp][fi];

            int tail = this.reversedCompressAndGetTail(compressConsumer, predicate);
            while (tail >= lastRow && matrixJellyCell[tail] != null) {
                matrixJellyCell[tail][i] = JellyBeanGenerator.randomJellyCell();
                tail--;
            }

            while (tail >= 0) {
                matrixJellyCell[tail] = JellyBeanGenerator.randomJellyCells(CELL_NUM);
                tail--;
            }
        }
    }

    /**
     * 定义为泛型可压缩一维或二维数组
     */
    private int reversedCompressAndGetTail(BiConsumer<Integer, Integer> compressConsumer,
                                           IntPredicate predicate) {
        int fast = CELL_NUM - 1;
        int slow = fast;
        while (fast >= 0 && slow >= 0) {
            if (predicate.test(fast)) {
                fast--;
                continue;
            }

            compressConsumer.accept(slow, fast);
            fast--;
            slow--;
        }

        return slow;
    }

    private void compressTotalRow() {
        JellyCell[][] matrixJellyCell = this.jellyContext.getMatrixJellyCell();
        BiConsumer<Integer, Integer> compressConsumer = (slowTemp, fastTemp) -> {
            matrixJellyCell[slowTemp] = matrixJellyCell[fastTemp];
        };
        IntPredicate predicate = value -> this.jellyContext.getTotalRowBombedSet().contains(value);
        int tail = this.reversedCompressAndGetTail(compressConsumer, predicate);
        while (tail >= 0) {
            matrixJellyCell[tail] = null;
            tail--;
        }
    }

}
